(function($) {
  
  /*
   * ajax spinner 관련 설정
   */
    var spinner,
        spinnerOpts = {
          lines: 13, // The number of lines to draw
          length: 7, // The length of each line
          width: 4, // The line thickness
          radius: 10, // The radius of the inner circle
          corners: 1, // Corner roundness (0..1)
          rotate: 0, // The rotation offset
          color: '#000', // #rgb or #rrggbb
          speed: 1, // Rounds per second
          trail: 60, // Afterglow percentage
          shadow: false, // Whether to render a shadow
          hwaccel: false, // Whether to use hardware acceleration
          className: 'spinner', // The CSS class to assign to the spinner
          zIndex: 2e9, // The z-index (defaults to 2000000000)
          top: 'auto', // Top position relative to parent in px
          left: 'auto' // Left position relative to parent in px
        };
    
    $(document)
      .ajaxSend(function () {
        if ( ! spinner) {
          spinner = new Spinner(spinnerOpts).spin(document.getElementById('map'));
        } else {
          spinner.spin(document.getElementById('map'));
        }
      })
      .ajaxComplete(function (event, xhr, options) {
        if (spinner) {
          spinner.stop();
        }
      });
  
  
  /*
   * 변수
   */
    var chartData = {},
        districts = [
        '강남구',
        '강동구',
        '강북구',
        '강서구',
        '관악구',
        '광진구',
        '구로구',
        '금천구',
        '노원구',
        '도봉구',
        '동대문구',
        '동작구',
        '마포구',
        '서대문구',
        '서초구',
        '성동구',
        '성북구',
        '송파구',
        '양천구',
        '영등포구',
        '용산구',
        '은평구',
        '종로구',
        '중구',
        '중랑구'
      ],
      seoulDataAPIKey = '4150495f32303830796f756e6767756b30323132',
      dataAPIBase = 'http://openapi.seoul.go.kr:8088/' + seoulDataAPIKey + '/json/DailyAverageAirQuality/1/100/',
      leafletAPIKey = 'd4fc77ea4a63471cab2423e66626cbb6',
      i, j, map, layer, seoulDistricts,
      container = $('#container'),
      todayDateObj = new Date(+(new Date()) - 86400000),    // API에 오늘 날짜 데이터가 없을 수도 있기 때문에 하루 전 데이터를 최신으로 함
      todayDateStr = (makeDateString(todayDateObj))[0],
      targetDateObj = todayDateObj,
      targetDateStr = todayDateStr;
      
    var getColor = {
      // 이산화질소농도
      NO2: function(d) {
        d = +(d) + 0.0001;
        
        return d > 0.60 ? '#B30000' :
               d > 0.20 ? '#E34A33' :
               d > 0.15 ? '#FC8D59' :
               d > 0.06 ? '#FDBB84' :
               d > 0.03 ? '#FDD49E' :
                          '#FEF0D9';
      },
      // 오존농도
      O3: function(d) {
        d = +(d) + 0.0001;
        
        return d > 0.5 ? '#54278F' :
               d > 0.3 ? '#756BB1' :
               d > 0.12 ? '#9E9AC8' :
               d > 0.08 ? '#BCBDDC' :
               d > 0.04 ? '#DADAEB' :
                          '#F2F0F7';
      },
      // 일산화탄소농도
      CO: function(d) {
        d = +(d) + 0.0001;
        
        return d > 30 ? '#993404' :
               d > 15 ? '#D95F0E' :
               d > 12 ? '#FE9929' :
               d > 9 ? '#FEC44F' :
               d > 2 ? '#FEE391' :
                         '#FFFFD4';
      },
      // 아황산가스
      SO2: function(d) {
        d = +(d) + 0.0001;
        
        return d > 0.4 ? '#006D2C' :
               d > 0.15 ? '#31A354' :
               d > 0.1 ? '#74C476' :
               d > 0.05 ? '#A1D99B' :
               d > 0.02 ? '#C7E9C0' :
                           '#EDF8E9';
      },
      // 미세먼지
      PM10: function(d) {
        d = +(d) + 0.0001;
        
        return d > 300 ? '#7A0177' :
               d > 200 ? '#C51B8A' :
               d > 120 ? '#F768A1' :
               d > 80 ? '#FA9FB5' :
               d > 30 ? '#FCC5C0' :
                        '#FEEBE2';
      }
    };
  
  
  /*
   * 함수
   */
    // 서울 열린데이터 광장에서 데이터 받아오기
    function drawMap(date) {
      
      if ( ! chartData[date]) {
        chartData[date] = {};
        
        var requestUrl;
       
        requestUrl = 'api.php?date=' + date;
        
        $.getJSON(requestUrl, function(data) {
          if (typeof data.DailyAverageAirQuality == 'undefined' || ( ! data.DailyAverageAirQuality)) {
            alert('데이터를 가져오는 중 문제가 생겼습니다. 잠시뒤에 다시 시도해 주십시오');
          } else {
            for (i = 0; i < data.DailyAverageAirQuality.list_total_count; i++) {
              if ($.inArray(data.DailyAverageAirQuality.row[i].MSRSTE_NM, districts) > -1) {
                chartData[date][data.DailyAverageAirQuality.row[i].MSRSTE_NM] = data.DailyAverageAirQuality.row[i];
              }
            }
            
            drawMap2(date);
          } 
        });
      } else {
        drawMap2(date);
      }
      
    }
    
    // 받아온 데이터를 서울 구경계 GeoJSON와 합침
    function drawMap2(date) {
      if ( ! seoulDistricts) {
        $.getJSON('data/seoul_districts.json', function(data) {
          seoulDistricts = data;
          
          var len = data.features.length;
          
          for (i = 0; i < len; i++) {
            $.extend(data.features[i].properties, chartData[date][data.features[i].properties.name]);
          }
          
          drawColor(data);
        });
      } else {
        var data = seoulDistricts,
            len = data.features.length;
          
          for (i = 0; i < len; i++) {
            $.extend(data.features[i].properties, chartData[date][data.features[i].properties.name]);
          }
          
          drawColor(data);
      }
    }
    
    // 화면에 색상 표시
    function drawColor(data) {
      if ( ! data) {
        return;
      }
      
      if (map && layer) {
        map.removeLayer(layer);
      }
      
      layer = L.geoJson(data.features, {
        style: style,
        onEachFeature: onEachFeature
      }).addTo(map);
      
      $('div.legend').find('.item').not('.source').hide();
      $('div.legend').find('.item').filter('.' + type).show();
    }
    
    // 각각의 구에 이벤트 등록
    function onEachFeature(feature, layer) {
      layer.on({
        mouseover: highlightFeature,
        mouseout: resetHighlight,
        click: zoomToFeature
      });
    }

    // 구에 마우스 올렸을 때 색상 강조  
    function highlightFeature(e) {
      var layer = e.target;
  
      layer.setStyle({
          weight: 2,
          color: '#777',
          dashArray: '',
          fillOpacity: 0.7
      });
  
      if (!L.Browser.ie && !L.Browser.opera) {
        layer.bringToFront();
      }
      
      info.update(layer.feature.properties);
    }
    
    // 구에서 마우스 내렸을 때 색상 복원
    function resetHighlight(e) {
      layer.resetStyle(e.target);
      
      info.update();
    }
    
    var zoomToFeature;
    
    if ('ontouchstart' in window) {
      // 구를 클릭했을 때 확대
      zoomToFeature = function(e) {
        var layer = e.target;
    
        if (!L.Browser.ie && !L.Browser.opera) {
          layer.bringToFront();
        }
        
        info.update(layer.feature.properties);
        
        map.fitBounds(e.target.getBounds());
      }
    } else {
      // 구를 클릭했을 때 확대
      zoomToFeature = function(e) {
        map.fitBounds(e.target.getBounds());
      }
    }
    
    // 구 색상 표시
    function style(feature) {
      var type = $('#control').find('input:checked')[0].value;
      
        return {
          fillColor: getColor[type](feature.properties[type]),
          weight: 2,
          opacity: 1,
          color: 'white',
          dashArray: '3',
          fillOpacity: 0.7
        };
    }
    
    // 날짜 문자열 만들기
    function makeDateString(dateObj) {
      var year = dateObj.getFullYear(),
          month = dateObj.getMonth() + 1 < 10 ? '0' + (dateObj.getMonth() + 1) : dateObj.getMonth() + 1,
          date = dateObj.getDate() < 10 ? '0' + dateObj.getDate() : dateObj.getDate(),
          dateStr = year + '' + month + '' + date;
          
      return [dateStr, year, month, date];
    }
    
    // 날짜 슬라이더 우측에 선택된 날짜 표시
    function showDate(dateStr) {
      $('#control').find('.date').text(dateStr.substr(0, 4) + '.' + dateStr.substr(4, 2) + '.' + dateStr.substr(6, 2));
    }
    
    // 날짜 슬라이더 만들기
    function makeSliderDates(dateObj) {
      var dateArray = makeDateString(dateObj),
          monthNum = parseInt(dateArray[2], 10),
          year,
          month,
          yearMonths = [],
          i = 0,
          span,
          diff,
          left;
          
      for (; i < 5; i++) {
        year = dateArray[1];
        month = monthNum - 2 * (i + 1);
        if (month < 1) {
          year -= 1;
          month += 12;
        }
        
        diff = parseInt((+(dateObj) - +(new Date(year, (month - 1)))) / 86400000, 10);
        left = (365 - diff) * 100 / 365;
        
        if (month < 10) {
          month = '0' + month;
        } else {
          month = '' + month;
        }
        
        span = $('<span />')
          .attr('class', 'year-month')
          .text(year + '.' + month)
          .css('left', (left).toFixed(1) + '%')
          .append('<span class="bar"></span>');
        span.appendTo('#slider-container');
        span.data('value', 365 - diff);
      }
    }
    
    // 날짜 슬라이더 콜백
    function sliderChangeCb(value) {
      var dateObj = new Date(+(todayDateObj) - (365 - value) * 86400000),
          dateStr = (makeDateString(dateObj))[0];
          
      container.trigger('dateChange', [dateStr]);
    }
  
  
  /*
   * 이벤트 핸들러
   */  
    // 날짜 변경될 때
    container.on('dateChange', function(e, dateStr) {
      targetDateObj = new Date(dateStr.substr(0, 4), dateStr.substr(4, 2), dateStr.substr(6, 2));
      targetDateStr = (makeDateString(targetDateObj))[0];
      
      showDate(dateStr);
      drawMap(dateStr);
    });
    
    // 날짜 슬라이더 아래에 날짜 클릭할 때
    $('#slider-container').on('click', '.year-month', function() {
      var value = $(this).data('value');
      
      sliderChangeCb(value);
      $('#slider').slider('value', value);
    });
    
    // 오염물질 선택할 때
    $('#control').find('input').on('change', function() {
      type = $('#control').find('input:checked')[0].value;
      typeText = $('#control').find('input:checked').next('label').text();
      $('#type-text').text(typeText);
      
      drawMap(targetDateStr);
    });
  
  
  /*
   * 구성요소 만들기
   */    
    // 오염물질 선택 버튼
    $('#control').buttonset();
    
    // 날짜 슬라이더
    $('#slider').slider({
      value: 365,
      min: 1,
      max: 365,
      step: 1,
      slide: function(event, ui) {
        sliderChangeCb(ui.value);
      },
      change: function(event, ui) {
        sliderChangeCb(ui.value);
      }
    });
    
    // 지도
    map = L.map('map').setView([37.56022, 127.01], 11);
    
    L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {
      key: leafletAPIKey,
      attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
      styleId: 22677,
    }).addTo(map);
  
    // 지도 우측 상단에 정보표시
    var info = L.control();
    var type = $('#control').find('input:checked')[0].value,
        typeText = $('#control').find('input:checked').next('label').text(),
        units = {
          'NO2': 'ppm',
          'O3': 'ppm',
          'CO': 'ppm',
          'SO2': 'ppm',
          'PM10': '㎍/㎥'
        };
  
    info.onAdd = function (map) {
        this._div = L.DomUtil.create('div', 'info');
        this.update();
        return this._div;
    };
    
    info.update = function (props) {
        this._div.innerHTML = '<h4>대기오염 물질: <span id="type-text">' + typeText + '</span></h4>' +  (props ?
            '<strong>' + props.MSRSTE_NM + '</strong>: ' + props[type] + ' ' + units[type]
            : '구 위에 마우스를 올려 보세요');
    };
    
    info.addTo(map);
  
    // 범례
    var legend = L.control({position: 'bottomright'});
  
    legend.onAdd = function (map) {
    
      var div = L.DomUtil.create('div', 'info legend'),
          grades = {},
          labels = [];
          
      grades['NO2'] = ['0', '0.030', '0.060', '0.150', '0.200', '0.600'];
      grades['O3'] = ['0', '0.040', '0.080', '0.120', '0.300', '0.500'];
      grades['CO'] = ['0', '2.00', '9.00', '12.00', '15.00', '30.00'];
      grades['SO2'] = ['0', '0.020', '0.050', '0.100', '0.150', '0.400'];
      grades['PM10'] = ['0', '30', '80', '120', '200', '300'];
  
      for (var type in grades) {
        div.innerHTML += '<div class="item unit ' + type + '">단위: ' + units[type] + '</div>';
        for (var i = 0; i < grades[type].length; i++) {
            div.innerHTML +=
                '<div class="item ' + type + '"><i style="background-color:' + getColor[type](grades[type][i]) + '"></i> ' +
                grades[type][i] + (grades[type][i + 1] ? ' &ndash; ' + grades[type][i + 1] + '</div>' : '+</div>');
        }
      }
      
      div.innerHTML += '<div class="item source"><a href="http://cleanair.seoul.go.kr/inform.htm?method=cai" target="_blank">통합대기환경지수(CAI)</a></div>'
  
      return div;
    };
    
    legend.addTo(map);
  
    
  /*
   * 최초 로딩시 실행
   */
    // 오늘 날짜 표시
    showDate(todayDateStr);
    
    // 날짜 슬라이더 만들기
    makeSliderDates(todayDateObj);
    
    // 지도 그리기
    drawMap(targetDateStr);
  
})(window.jQuery);
